﻿using System;
using System.ComponentModel;
using System.Globalization;
using System.Xml.Linq;
using System.Xml.Schema;
using Automator.Runtime.Classes;

namespace Automator.Runtime
{
    /// <summary>
    /// Description of RuntimeEntity.
    /// </summary>
    public class RuntimeEntity: INotifyPropertyChanged
    {
        
        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string name)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(name));
        }

        #endregion
        
        #region Fields
        
        private string _fileName;
        private bool _modified;
        private EntityExecutingState _state = EntityExecutingState.Ready;
        private Guid _id = Guid.Empty;

        #endregion
        
        #region Properties
        
        /// <summary>
        /// Database ID of the entity
        /// </summary>
        public Guid Id
        {
            get { return _id; }
        }
        
        /// <summary>
        /// Full name of the entity file
        /// </summary>
        public string FileName
        {
            get { return _fileName; }
            set
            {
                _fileName = value;
                Modified = true;
            }
        }

        public virtual string Name
        {
            get { return _fileName; }
            set { OnPropertyChanged("Name"); }
        }

        public bool Modified
        {
            get { return _modified; }
            set
            {
                _modified = value;
                if (ModifiedChanged != null)
                    ModifiedChanged();
            }
        }

        public Logger Log { get; private set; }

        /// <summary>
        /// Current state of the entity object
        /// </summary>
        public EntityExecutingState State
        {
            get { return _state; }
            set { _state = value; }
        }
        
        protected XDocument XML { get; set; }
        
        protected XmlSchemaSet XSDSchema { get; set; }
        
        protected string FileFormatCommentString { get; set; }

        #endregion
        
        #region Methods
        
        public RuntimeEntity()
        {
            this.FileName = String.Empty;
            this.Modified = false;
            this.Log = new Logger();
            
            this.ModifiedChanged += delegate { if (FileNameChanged != null) FileNameChanged(); };
            this.ModifiedChanged += delegate { OnPropertyChanged("Name"); };
        }
        
        public virtual XDocument Serialize()
        {
            return new XDocument(new XElement("RuntimeEntity"));
        }
        
        public virtual void Run()
        {
            
        }
        
        /// <summary>
        /// Loads entity from file
        /// </summary>
        /// <param name="xmlPath">Full path to the entity file</param>
        public void Load(string xmlPath)
        {
            this.FileName = xmlPath;
            this.XML = XDocument.Load(xmlPath);
            this.ClearId();
            Deserialize();
            this.Modified = false;
        }
        
        /// <summary>
        /// Loads entity from database
        /// </summary>
        /// <param name="entityId">Database Id of the entity</param>
        /// <param name="entityName">Name of the entity</param>
        /// <param name="entityXml">XML string of the entity</param>
        public void Load(Guid entityId, string entityName, string entityXml)
        {
            AssignId(entityId);
            this._fileName = entityName;
            this.XML = XDocument.Parse(entityXml);           
            Deserialize();
            this.Modified = false;
        }
        
        protected virtual void Deserialize()
        {
            try
            {
                AnalyzeFile();
            }
            catch (Exception ex)
            {
                this.Log.AddLog(LogMessageType.Error, ex.Message);
            }             
        }
        
        public void ClearFileName()
        {
            _fileName = String.Empty;
        }
        
        protected void ValidateCallback(object sender, ValidationEventArgs e)
        {
            this.Log.AddLog(LogMessageType.Warning, String.Format("Validation: {0}", e.Message));
        }
        
        /// <summary>
        /// Method that performs analyzing of file format and validation of XML document
        /// </summary>
        protected void AnalyzeFile()
        {
            if (XML != null)
            {
                foreach (XNode node in XML.Nodes())
                {
                    if (node is XComment && (node as XComment).Value.Contains(FileFormatCommentString))
                    {
                        NumberFormatInfo numberInfo = new NumberFormatInfo();
                        double fileFormat = Convert.ToDouble((node as XComment).Value.Replace(FileFormatCommentString, String.Empty), numberInfo);
                        double actualFormat = Convert.ToDouble(XmlValidation.CurrentFormatVersion, numberInfo);
                        if (fileFormat < actualFormat)
                            this.Log.AddLog(LogMessageType.Warning, "This file was created in older version of Automator, actual version can have some breaking changes");
                        else if (fileFormat > actualFormat)
                            throw new Exception("This file was created in a newer version of Automator");
                        break;
                    }
                }
                
                if (this.XSDSchema != null)
                    XML.Validate(this.XSDSchema, this.ValidateCallback);
                else
                    this.Log.AddLog(LogMessageType.Warning, "Cannot validate, XSD schema is not loaded");
            }
            else
                throw new Exception("XML document not found");
        }
        
        public void CreateId()
        {
            _id = Guid.NewGuid();
        }
        
        public void ClearId()
        {
            _id = Guid.Empty;
        }
        
        protected void AssignId(string newId)
        {
            if (!String.IsNullOrEmpty(newId))
            {
                Guid newGuid;
                if (Guid.TryParse(newId, out newGuid) && !newGuid.Equals(Guid.Empty))
                    AssignId(newGuid);
            }
        }
        
        protected void AssignId(Guid newId)
        {
            _id = newId;
        }
        
        #endregion
        
        #region Events

        public event FileNameChangedHandler FileNameChanged;
        public event ModifiedChangedHandler ModifiedChanged;

        #endregion
        
    }
}
